using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Text.RegularExpressions;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace Shifter
{
    interface IParser
    {
        List<Rule> ParseXML();
        String FindRule(String node);
    }

    public class Parser : IParser
    {
        //consts
        private const int RULE_NAME_INDEX = 6;
        private const int MIN_RULESTEP_PARAMS = 4;

        //members
        private XmlDocument m_FDRulesFile;
        private List<Rule> m_rulesList;
        private String m_rules;

        public Parser(string filePath)
        {
            m_rulesList = new List<Rule>();
            m_FDRulesFile = new XmlDocument();
            m_rules = "";
            m_FDRulesFile.Load(filePath);
        }

        private void checkContent()
        {
            Console.WriteLine("\r\nContent Check!");
            foreach (Rule rule in m_rulesList)
            {
                Console.WriteLine("   " + rule.Name + " - " + rule.RuleStepList.Count + " rule step(s)");
            }
        }

        //probably should return something other than void -> TBD
        public void ParseXML( XmlDocument doc )
        { }

        //probably should return something other than void -> TBD - String?
        public void ParseRule( XmlNode node )
        { }

        String IParser.FindRule(String name)
        {
            String retVal = "temp";
            String temp = "(RULE " + name;

            int startIndex = m_rules.IndexOf(temp);
            int endIndex = findRuleEnd(startIndex);

            retVal = m_rules.Substring(startIndex, endIndex - startIndex + 1);

            return retVal;
        }

        private int findRuleEnd(int startIndex)
        {
            int retVal = 0;
            int firstClosing = m_rules.IndexOf(')', startIndex);

            while (0 == retVal)
            {
                int nextClosing = m_rules.IndexOf(')', firstClosing + 1);

                String temp = m_rules.Substring(firstClosing, nextClosing - firstClosing + 1);
                temp = temp.Trim();
                temp = temp.Replace("\t", "");
                temp = temp.Replace("\r\n", "");

                Console.WriteLine("temp Len: {0}", temp.Length);
                Console.WriteLine("tempLine: \'{0}\'", temp);

                if (temp.Length <= 2 )
                {
                    retVal = nextClosing;
                }

                firstClosing = nextClosing;
            }

            return retVal;
        }

        List<Rule> IParser.ParseXML()
        {
            Console.WriteLine("IParser.ParseXML() called");

            ParseNodes(m_FDRulesFile);
            checkContent();

            return m_rulesList;
        }

        private void FindComment(XmlNode node)
        { }

        private void ParseNodes(XmlNode node)
        {
            foreach (XmlNode kid in node.ChildNodes)
            {
                Console.WriteLine("node name: " + kid.Name.ToString());
                if (XmlNodeType.Element == kid.NodeType && kid.Name.Equals("RULES"))
                {
                    m_rules += kid.InnerXml;
                    FindRules(kid.InnerText, 0); // 0 means start from the begining
                }
                else
                {
                    if (kid.HasChildNodes)
                    {
                        ParseNodes(kid);
                    }
                }
            }
        }

        private void FindRules(String rules, int start)
        {
            int ruleBeginIndex = rules.IndexOf("(RULE ", start);
            int nameBegin = ruleBeginIndex + RULE_NAME_INDEX;
            int ruleEndIndex = 0;
            char[] whiteSpace = " \r\n\t".ToCharArray();

            if (-1 == ruleBeginIndex)
            {
                Console.WriteLine("Rules not found in node");
            }
            else
            {
                int nameEnd = rules.IndexOfAny(whiteSpace, nameBegin);
                String ruleName = rules.Substring(nameBegin, nameEnd - nameBegin);

                m_rulesList.Add(new Rule(ruleName));
                ruleEndIndex = findRuleSteps(ruleBeginIndex, rules);

                FindRules(rules, ruleEndIndex);
            }
        }

        private int findRuleSteps(int ruleBeginIndex, String rules)
        {
            int openParenthesis = 0;
            int rsStartIndex = 0;
            int rsEndIndex = 0;
            char[] parenthesisTab = {'(', ')'};
            int index = rules.IndexOfAny(parenthesisTab, ruleBeginIndex);
            String tempLine;

            while (0 < index)
            {
                if (rules[index].Equals('('))
                {
                    ++openParenthesis;

                    if (2 == openParenthesis)
                    {
                        rsStartIndex = index;
                    }
                }
                else if (rules[index].Equals(')'))
                {
                    --openParenthesis;

                    if (1 == openParenthesis)
                    {
                        rsEndIndex = index;
                        tempLine = rules.Substring(rsStartIndex, rsEndIndex - rsStartIndex);
                        tempLine = tempLine.Trim(parenthesisTab);

                        RuleStep newRuleStep = parseRuleStep(tempLine);
                        if (0 < m_rulesList.Count)
                        {
                            m_rulesList[m_rulesList.Count - 1].RuleStepList.Add(newRuleStep);
                        }
                        else
                        {
                            Console.WriteLine("ERROR: Rule list is empty");
                            throw (new IndexOutOfRangeException("list is empty"));
                        }
                    }
                }
                else
                {
                    throw new Exception("Parentesis count fail");
                }

                if (0 == openParenthesis)
                {
                    break;
                }
                else
                {
                    index = rules.IndexOfAny(parenthesisTab, index + 1);
                }
            }

            return index;
        }

        // RuleStep syntax: (0; IsMemberType (@FaultSource; FSP; FARADAY) -1; -2)
        private RuleStep parseRuleStep(String line)
        {
            String[] split = line.Split(new char[] { ';', '(', ')' });

            RuleStep currentRuleStep = new RuleStep();
            currentRuleStep.StepNumber = Convert.ToInt32(split[0]);
            currentRuleStep.Routine = split[1];
            currentRuleStep.FalseStep = Convert.ToInt32(split[split.Length - 1]);
            currentRuleStep.TrueStep = Convert.ToInt32(split[split.Length - 2]);

            //parsing args if there are any
            if (MIN_RULESTEP_PARAMS < split.Length)
            {
                for (int i = 2; i < split.Length - 2; ++i)
                {
                    currentRuleStep.ArgsList.Add(split[i]);
                }
            }

            return currentRuleStep;
        }

    }

    class Rule
    {
        //members
        private String m_name;
        private List<RuleStep> m_RuleStepList;

        //constructors
        public Rule(String name)
        {
            m_name = name;
            m_RuleStepList = new List<RuleStep>();
        }

        public Rule(Rule input)
        {
            this.m_name = input.Name;
            this.m_RuleStepList = CloneList(input.RuleStepList);
        }

        //properties
        public String Name
        {
            get { return m_name; }
            //set { m_name = value; }
        }

        public List<RuleStep> RuleStepList
        {
            get { return m_RuleStepList; }
            set { m_RuleStepList = value; }
        }

        //priv methods
        private List<RuleStep> CloneList(List<RuleStep> oldList)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            formatter.Serialize(stream, oldList);
            stream.Position = 0;
            return (List<RuleStep>)formatter.Deserialize(stream);
        }
    }

    [System.SerializableAttribute()]
    class RuleStep
    {
        //members
        private int m_stepNumber;
        private String m_routine;
        private List<String> m_argsList;
        private int m_trueStep;
        private int m_falseStep;

        //constructor
        public RuleStep()
        {
            m_stepNumber = -1;
            m_routine = "notSet";
            m_argsList = new List<String>();
            m_trueStep = -1;
            m_falseStep = -1;
        }

        public RuleStep(RuleStep input)
        {
            this.m_stepNumber = input.StepNumber;
            this.m_routine = input.Routine;
            this.m_argsList = new List<String>(input.ArgsList);
            this.m_trueStep = input.TrueStep;
            this.m_falseStep = input.FalseStep;
        }

        //operators
        public static bool operator==(RuleStep rhs, RuleStep lhs)
        {
            bool retVal = true;
            if (rhs.StepNumber != lhs.StepNumber)
            {
                retVal = false;
            }
            else if (rhs.Routine != lhs.Routine)
            {
                retVal = false;
            }
            else if (rhs.ArgsListToGrid != lhs.ArgsListToGrid)
            {
                retVal = false;
            }
            else if (rhs.TrueStep != lhs.TrueStep)
            {
                retVal = false;
            }
            else if (rhs.FalseStep != rhs.FalseStep)
            {
                retVal = false;
            }

            return retVal;
        }

        public static bool operator!=(RuleStep rhs, RuleStep lhs)
        {
            return !(rhs == lhs);
        }

        public override bool Equals(Object arg)
        {
            return (this == (RuleStep)arg);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        
        //properties
        public int StepNumber
        {
            get { return m_stepNumber; }
            set { m_stepNumber = value; }
        }

        public String Routine
        {
            get { return m_routine; }
            set { m_routine = value; }
        }
        
        public List<String> ArgsList
        {
            get { return m_argsList; }
            set { m_argsList = value; }
        }

        public String ArgsListToGrid
        {
            get
            {
                if (0 < m_argsList.Count)
                {
                    return "( " + String.Join("; ", m_argsList.ToArray()) + " )";
                }
                else
                {
                    return "None";
                }
            }
        }
        
        public int TrueStep
        {
            get { return m_trueStep; }
            set { m_trueStep = value; }
        }

        public int FalseStep
        {
            get { return m_falseStep; }
            set { m_falseStep = value; }
        }
    }

    class ModyfiedRuleStep
    {
        //members
        private bool m_added;
        private RuleStep m_orginal;
        private RuleStep m_modyfied;

        //ctor
        ModyfiedRuleStep(RuleStep org, RuleStep mod)
        {
            m_added = false;
            m_orginal = org;
            m_modyfied = mod;
        }

        ModyfiedRuleStep(RuleStep mod)
        {
            m_added = true;
            m_orginal = new RuleStep();
            m_modyfied = mod;
        }

        public bool Added
        {
            get { return m_added; }
        }

        public RuleStep Orginal
        {
            get { return m_orginal; }
        }

        public RuleStep Modyfied
        {
            get { return m_modyfied; }
        }
    }

    
}
